﻿using System;
using System.Collections.Generic;
using System.Xml;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace TFSTools.TFSLibrary
{
    public sealed class TFSWorkItemStore : TFSServerEntity
    {
        #region Initialization

        private WorkItemStore serverWorkItemStore;

        public TFSWorkItemStore(ITFSConnection connection) : base(connection)
        {
            connection.CheckConnection();
            this.serverWorkItemStore = new WorkItemStore(connection.SelectedTeamProjectCollection);

            
        }

        internal WorkItemStore ServerEntity { get { return this.serverWorkItemStore; } }

        #endregion

        #region Import/Export Definition

        public XmlDocument ExportDefinition(string typeName)
        {
            this.Connection.CheckConnection();

            Project teamProject = this.serverWorkItemStore.Projects[this.Connection.SelectedProject.Name];

            WorkItemType type = teamProject.WorkItemTypes[typeName];

            return type.Export(false);
        }

        public void ImportDefinition(string definition)
        {
            this.Connection.CheckConnection();

            Project teamProject = this.serverWorkItemStore.Projects[this.Connection.SelectedProject.Name];

            teamProject.WorkItemTypes.Import(definition);
        }

        #endregion

        #region Work Items

        public TFSWorkItem CreateWorkItem(TFSWorkItemType workItemType)
        {
            this.Connection.CheckConnection();

            WorkItem workItem = workItemType.ServerEntity.NewWorkItem();
            TFSWorkItem tfsWorkItem = new TFSWorkItem(this.Connection, workItem);

            return tfsWorkItem;
        }

        public TFSWorkItem CreateWorkItem(string typeName)
        {
            return this.CreateWorkItem(this.GetWorkItemType(typeName));
        }

        public TFSWorkItem FindWorkItem(int id)
        {
            this.Connection.CheckConnection();

            WorkItem wi = this.serverWorkItemStore.GetWorkItem(id);

            return new TFSWorkItem(this.Connection, wi);
        }

        public TFSWorkItem CopyWorkItem(TFSWorkItem workItem)
        {
            return new TFSWorkItem(this.Connection, workItem.ServerEntity.Copy());
        }

        public TFSWorkItem CreateRelatedWorkItem(TFSWorkItem workItem)
        {
            TFSWorkItem newWorkItem = this.CreateWorkItem(workItem.WorkItemType);

            WorkItem oldTFSWorkItem = workItem.ServerEntity;
            WorkItem newTFSWorkItem = newWorkItem.ServerEntity;

            newTFSWorkItem.Title = string.Format("Related to Work Item {0} - {1}", oldTFSWorkItem.Id, oldTFSWorkItem.Title);
            newTFSWorkItem.IterationId = oldTFSWorkItem.IterationId;
            newTFSWorkItem.IterationPath = oldTFSWorkItem.IterationPath;
            newTFSWorkItem.AreaId = oldTFSWorkItem.AreaId;
            newTFSWorkItem.AreaPath = oldTFSWorkItem.AreaPath;
            newTFSWorkItem.Description = oldTFSWorkItem.Description;

            RelatedLink linkToNew = new RelatedLink(oldTFSWorkItem.Id);
            newTFSWorkItem.Links.Add(linkToNew);

            return newWorkItem;
        }

        public void AddRelatedLinkToWorkItem(TFSWorkItem sourceWorkItem, TFSWorkItem targetWorkItem, string comment)
        {
            RelatedLink link = new RelatedLink(sourceWorkItem.Id);
            link.Comment = comment;
            targetWorkItem.ServerEntity.Links.Add(link);
        }

        #endregion

        #region Field Definitions

        public TFSFieldDefinition GetFieldDefinitionFromReferenceName(string fieldReferenceName)
        {
            var fields = this.serverWorkItemStore.FieldDefinitions;

            foreach (FieldDefinition field in fields)
            {
                if (field.ReferenceName == fieldReferenceName)
                {
                    return new TFSFieldDefinition(this.Connection, field);
                }
            }

            return new TFSFieldDefinition(this.Connection, fields[fieldReferenceName]);
        }

        #endregion

        #region Work Item Types

        public TFSWorkItemType GetWorkItemType(string typeName)
        {
            this.Connection.CheckConnection();

            Project teamProject = this.serverWorkItemStore.Projects[this.Connection.SelectedProject.Name];

            var types = new System.Collections.ArrayList(teamProject.WorkItemTypes);

            return new TFSWorkItemType(this.Connection, teamProject.WorkItemTypes[typeName]);
        }

        public TFSWorkItemTypeCollection GetWorkItemTypes()
        {
            this.Connection.CheckConnection();

            Project teamProject = this.serverWorkItemStore.Projects[this.Connection.SelectedProject.Name];

            WorkItemTypeCollection col = teamProject.WorkItemTypes;

            return TFSWorkItemTypeCollection.Create(this.Connection, col);
        }

        #endregion

        #region Queries

        private TFSWorkItemCollection RunQuery(string queryText, bool includeTimePrecision)
        {
            this.Connection.CheckConnection();

            queryText = this.ConvertQueryTags(queryText);

            if (includeTimePrecision)
            {
                Query qry = new Query(this.serverWorkItemStore, queryText, null, false);

                ICancelableAsyncResult car = qry.BeginQuery();
                return TFSWorkItemCollection.Create(this.Connection, qry.EndQuery(car));
            }
            else
            {
                return TFSWorkItemCollection.Create(this.Connection, this.serverWorkItemStore.Query(queryText));
            }
        }

        public TFSWorkItemCollection Query(string queryText)
        {
            return this.RunQuery(queryText, false);
        }

        public TFSWorkItemCollection Query(string queryText, bool includeTimePrecision)
        {
            return this.RunQuery(queryText, includeTimePrecision);
        }

        public TFSWorkItemCollection Query(string queryText, DateTime asOf)
        {
            queryText += " asof '" + asOf.ToString() + "'";

            return this.RunQuery(queryText, false);
        }

        public TFSWorkItemCollection Query(string queryText, DateTime asOf, bool includeTimePrecision)
        {
            queryText += " asof '" + asOf.ToString() + "'";

            return this.RunQuery(queryText, includeTimePrecision);
        }

        private string ConvertQueryTags(string queryText)
        {
            while (queryText.Contains("@project"))
            {
                queryText = queryText.Replace("@project", "'" + this.Connection.SelectedProject.Name + "'");
            }

            while (queryText.Contains("@me"))
            {
                queryText = queryText.Replace("@me", "'" + this.Connection.SelectedTeamProjectCollection.AuthorizedIdentity.DisplayName + "'");
            }

            return queryText;
        }

        public TFSWorkItemCollection Query(TFSStoredQueryDefinition queryDefinition)
        {
            Dictionary<string, string> context = new Dictionary<string, string>(2);
            context.Add("me", this.Connection.SelectedTeamProjectCollection.AuthorizedIdentity.DisplayName);
            context.Add("project", queryDefinition.TeamProject.Name);

            Query query = new Query(this.serverWorkItemStore, queryDefinition.QueryText, context);

            if (query.IsLinkQuery)
            {
                WorkItemLinkInfo[] workItemLinkInfos = query.RunLinkQuery();

                var workItems = new TFSWorkItemCollection(this.Connection);
                
                Stack<int> parents = new Stack<int>();
                int previous = 0;
                foreach (WorkItemLinkInfo workItemLinkInfo in workItemLinkInfos)
                {
                    // Build indented query result tree
                    if (parents.Count == 0)
                    {
                        parents.Push(workItemLinkInfo.SourceId);
                    }

                    if (parents.Peek() != workItemLinkInfo.SourceId && previous == workItemLinkInfo.SourceId)
                    {
                        parents.Push(workItemLinkInfo.SourceId);
                    }
                    else
                    {
                        while (workItemLinkInfo.SourceId != parents.Peek())
                        {
                            parents.Pop();
                        }
                    }

                    previous = workItemLinkInfo.TargetId;

                    TFSWorkItem tfsWI = new TFSWorkItem(this.Connection, this.serverWorkItemStore.GetWorkItem(workItemLinkInfo.TargetId));
                    tfsWI.TreeLevel = parents.Count;

                    workItems.Add(tfsWI);
                }

                return workItems;
            }
            else
            {
                return TFSWorkItemCollection.Create(this.Connection, query.RunQuery());
            }
        }

        #endregion

        public override string ToString() { return this.serverWorkItemStore.ToString(); }
    }
}
